package de.koller.aframe.util.noise.n2D;

import de.koller.aframe.util.math.Function2DPreview;
import de.koller.aframe.util.math.FunctionCutView;
import de.koller.aframe.util.math.FunctionPreview;
import de.koller.aframe.util.math.IFunction;
import de.koller.oceanFramework.math.RVec2;
import de.koller.oceanFramework.math.RVec3;
import java.util.Random;

public class Perlin2D extends Noise2D {

	public static boolean PRINT_PROGRESS = false;
	
	private final Random rnd;
	private final float persistence;
	private final float frequenceFactor;
	private final int steps;
	private final int startStep;
	
	public Perlin2D( int startStep, int steps, float persistence, float frequenceFactor, Random rnd ) {
		super( noiseArray( startStep+steps ) );
		this.rnd = rnd;
		this.startStep = startStep;
		this.steps = steps;
		this.persistence = persistence;
		this.frequenceFactor = frequenceFactor;
		
		this.generate();
	}
	
	private void generate() {
		for( int i = startStep; i < steps+startStep; i++ )
			handleStep( i );
	}
	
	private void handleStep( int step ) {
		float amplitude = (float)Math.pow( persistence, step );
		int frequence = (int)Math.pow( frequenceFactor, step );
		
		Noise2D n = new Noise2D( rnd, frequence + 3 );

		for( int ax = 0; ax < noise.length; ax++ ) {
			for( int ay = 0; ay < noise.length; ay++ ) {
				float x = 1f / (float)noise.length * ax;
				float y = 1f / (float)noise.length * ay;
				
				noise[ax][ay] += n.getValue( x, y ) * amplitude;
			}
			if( PRINT_PROGRESS && ax % 100 == 0 ) {
				String progress = "berechnen: " + step + " / " + (steps+startStep) + 
						" size: " + ax + " / " + noise.length;
				System.out.println( progress );
			}
		}
	}
	
	public static float[][] noiseArray( int steps ) {
		int s = (int)Math.pow( 2, steps ) + 3;
		return new float[ s ][ s ];
	}
	
	public static float maxHeight( int startDetail, int details, float persistence ) {
		double h = 0;
		for( int i = startDetail; i < startDetail+details; i++ )
			h += Math.pow( persistence, i );
		return (float)h;
	}
	
	public static void main( String[] args ) {
		
		Random r = new Random();
		Function2DPreview.COLORS = new int[] { 0xffffffff, 0xff000000 };
		Perlin2D.PRINT_PROGRESS = true;
		while( true ) {
			Perlin2D p = new Perlin2D( 0, 3, 0.5f, 2, r );
			//p.normalize();
			//p.normalizeMaxBound( maxHeight( p.startStep, p.steps, p.persistence ) );
			p.normalize( 1 );
			Function2DPreview.showFunction( 0, 0, 1, 1, p );
			
			FunctionPreview.dontscale( -.2f );
			FunctionPreview.showFunction( -0.2f, 2.5f, 
					FunctionCutView.cutView2D( p, 0, .5f, 1, .5f ),
					new Derivate( p ) );
		}
	}
	
	private static class Derivate implements IFunction {
		private final Noise2D noise;

		public Derivate( Noise2D noise ) {
			this.noise = noise;
		}

		@Override
		public float getValue( float x ) {
			RVec3 r = noise.getDerivate( x, .5f );
			RVec2 v = new RVec2( r.x, r.z );
			return (float)Math.toDegrees( v.getDirection() );
		}
	}
	
}
